/*
*
* ████████╗░█████╗░███████╗██████╗░░█████╗░
* ╚══██╔══╝██╔══██╗██╔════╝██╔══██╗██╔══██╗
* ░░░██║░░░██║░░██║█████╗░░██████╦╝███████║
* ░░░██║░░░██║░░██║██╔══╝░░██╔══██╗██╔══██║
* ░░░██║░░░╚█████╔╝██║░░░░░██████╦╝██║░░██║
* ░░░╚═╝░░░░╚════╝░╚═╝░░░░░╚═════╝░╚═╝░░╚═╝
* 
*  :: 九方通逊 ::   Copyright©2020 tofba.com  
* 
*/
package com.lmexpress.service.impl;

import java.io.File;
import java.net.URL;
import java.rmi.RemoteException;
import java.security.MessageDigest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.lmexpress.enums.ApiCredential;
import com.lmexpress.enums.ApiOrderError;
import com.lmexpress.enums.LabelSuffix;
import com.lmexpress.orm.ktswebservice.SfKtsServiceProxy;
import com.lmexpress.orm.openapi.ExpressOrderApi;
import com.lmexpress.orm.openapi.OrderInvoice;
import com.lmexpress.orm.rest.ApiLabelResponse;
import com.lmexpress.orm.rest.ApiOrderResponse;
import com.lmexpress.service.ApiService;
import com.lmexpress.util.ApiUtil;
import com.lmexpress.util.HttpClientPoolUtil;
import com.lmexpress.util.HttpClientResult;

/**
 * <顺丰KTS B2C>
 * @author  Henry(fba02)
 * @version  [版本号, 2020-12-09]
 * @see  [相关类/方法]
 * @since  [产品/模块版本]
 */
public class KTSClientB2C implements ApiService {
	private final static Logger LOGGER = LoggerFactory.getLogger(KTSClientB2C.class);
	private final static String ERR = "ERR";
	private final static String OK = "OK";
	private final static String LABEL = "http://sfapi.trackmeeasy.com/ruserver/api/getLabelUrl.action";

	@Override
	public ApiOrderResponse preProcessCreateOrder(ExpressOrderApi expressOrderApi) {
		return null;
	}

	@Override
	public String mapping(ExpressOrderApi expressOrderApi) {
		Map<String, String> apiCredentials = expressOrderApi.getApiCredentials();
		StringBuffer stringBuffer = new StringBuffer();
		stringBuffer.append("<Request service=\"OrderService\" lang=\"zh_CN\">");
		stringBuffer.append("<Head>" + apiCredentials.get(ApiCredential.KTS_B2C_NAME.getKey()) + "</Head>");
		stringBuffer.append("<Body>");
		stringBuffer.append("<Order orderid=\"" + expressOrderApi.getCustomerInvoiceCode() + "\"");
		stringBuffer.append(" express_type=\"" + apiCredentials.get(ApiCredential.KTS_B2C_EXPRESS_TYPE.getKey()) + "\"");
		stringBuffer.append(" platform_merchant_id=\"\"");
		stringBuffer.append(" platform_code=\"" + "0000" + "\"");
		stringBuffer.append(" platform_order_id=\"" + ApiUtil.generateUUID() + "\"");
		stringBuffer.append(" erp_code=\"" + "0000" + "\"");
		stringBuffer.append(" category=\"\"");

		String shipperCompanyName = expressOrderApi.getShipperCompanyName();
		stringBuffer.append(" j_company=\"" + (StringUtils.isBlank(shipperCompanyName) ? "" : shipperCompanyName) + "\"");
		String shipperName = expressOrderApi.getShipperName();
		stringBuffer.append(" j_contact=\"" + (StringUtils.isBlank(shipperName) ? "" : shipperName) + "\"");

		String phone = expressOrderApi.getShipperTelephone();
		if (StringUtils.isBlank(phone)) {
			phone = "";
		}
		stringBuffer.append(" j_mobile=\"" + phone + "\"");
		stringBuffer.append(" j_tel=\"" + phone + "\"");

		String shipperCity = expressOrderApi.getShipperCity();// 发件人城市,选填
		String shipperState = expressOrderApi.getShipperState();// 发件人州,选填
		String shipperAddress = expressOrderApi.getShipperAddress1();
		stringBuffer.append(" j_province=\"" + shipperState + "\"");
		stringBuffer.append(" j_city=\"" + shipperCity + "\"");
		stringBuffer.append(" j_address=\"" + shipperAddress + "\"");
		stringBuffer.append(" j_country=\"" + expressOrderApi.getShipperCountry() + "\"");
		stringBuffer.append(" j_county=\"\"");
		String shipperPostCode = expressOrderApi.getShipperPostCode();
		stringBuffer.append(" j_post_code=\"" + (StringUtils.isBlank(shipperPostCode) ? "" : shipperPostCode) + "\"");
		String consigneePhone = StringUtils.isBlank(expressOrderApi.getConsigneeMobile()) ? expressOrderApi.getConsigneeTelephone() : expressOrderApi.getConsigneeMobile();

		String compamy = expressOrderApi.getConsigneeCompanyName();
		if (StringUtils.isBlank(compamy)) {
			compamy = expressOrderApi.getConsigneeName();
		}
		stringBuffer.append(" d_company=\"" + compamy + "\"");
		stringBuffer.append(" d_contact=\"" + expressOrderApi.getConsigneeName() + "\"");
		stringBuffer.append(" d_country=\"" + expressOrderApi.getCountryCode() + "\"");
		stringBuffer.append(" d_province=\"" + expressOrderApi.getConsigneeState() + "\"");
		stringBuffer.append(" d_city=\"" + expressOrderApi.getConsigneeCity() + "\"");
		stringBuffer.append(" d_county=\"\"");
		stringBuffer.append(" d_post_code=\"" + expressOrderApi.getConsigneePostCode() + "\"");
		stringBuffer.append(" d_mobile=\"" + consigneePhone + "\"");
		stringBuffer.append(" d_tel=\"" + consigneePhone + "\"");
		stringBuffer.append(" d_address=\"" + expressOrderApi.getConsigneeAddress() + "\"");

		stringBuffer.append(" parcel_quantity=\"" + 1 + "\"");
		stringBuffer.append(" sendstarttime=\"\"");
		stringBuffer.append(" pay_method=\"" + 1 + "\"");
		List<OrderInvoice> orderInvoices = expressOrderApi.getOrderInvoices();
		Double totalValue = 0D;
		Double totalWeight = 0D;
		if (CollectionUtils.isNotEmpty(orderInvoices)) {
			for (OrderInvoice orderInvoice : orderInvoices) {
				totalValue += orderInvoice.getInvoiceAmount() == null ? 0D : orderInvoice.getInvoiceAmount();
				totalWeight += orderInvoice.getInvoiceWeight() == null ? 0D : orderInvoice.getInvoiceWeight();
			}
		}
		stringBuffer.append(" declared_value=\"" + totalValue + "\"");
		stringBuffer.append(" declared_value_currency=\"" + (StringUtils.isBlank(expressOrderApi.getCurrencyCode()) ? DEFAULT_CURRENCY : expressOrderApi.getCurrencyCode()) + "\"");
		stringBuffer.append(" cargo_total_weight=\"" + (expressOrderApi.getWeight() == null ? totalWeight : expressOrderApi.getWeight()) + "\"");
		stringBuffer.append(" d_delivery_code=\"\"");
		stringBuffer.append(" isBat=\"" + 0 + "\"");
		stringBuffer.append(" custid=\"\"");
		stringBuffer.append(" return_remark=\"\"");
		stringBuffer.append(" operate_flag=\"" + 1 + "\"");
		stringBuffer.append(" cargo_length=\"\"");
		stringBuffer.append(" cargo_width=\"\"");
		stringBuffer.append(" cargo_height=\"\"");
		stringBuffer.append(" remark=\"\"");
		stringBuffer.append(" cargo_des=\"\"");
		stringBuffer.append(" returnsign=\"\">");
		for (OrderInvoice orderInvoice : orderInvoices) {
			stringBuffer.append(" <Cargo");
			stringBuffer.append(" name=\"" + orderInvoice.getSkuNameEn() + "\"");
			stringBuffer.append(" count=\"" + orderInvoice.getInvoicePcs() + "\"");
			stringBuffer.append(" unit=\"" + "piece" + "\"");
			stringBuffer.append(" ename=\"" + (StringUtils.isBlank(orderInvoice.getSkuNameEn()) ? "" : orderInvoice.getSkuNameEn()) + "\"");
			stringBuffer.append(" cname=\"" + orderInvoice.getSkuNameCn() + "\"");
			stringBuffer.append(" weight=\"" + orderInvoice.getInvoiceWeight() + "\"");
			stringBuffer.append(" amount=\"" + orderInvoice.getInvoiceAmount() + "\"");
			stringBuffer.append(" cargo_desc=\"" + orderInvoice.getSkuCode() + "\"");
			stringBuffer.append(" currency=\"" + "USD" + "\"");
			stringBuffer.append("/>");
		}
		stringBuffer.append("</Order>");
		stringBuffer.append("</Body>");
		stringBuffer.append("</Request>");
		return stringBuffer.toString();
	}

	@Override
	public ApiOrderResponse createOrder(ExpressOrderApi expressOrderApi) {
		Map<String, String> apiCredentials = expressOrderApi.getApiCredentials();
		String xml = mapping(expressOrderApi);
		LOGGER.info("kts.b2c.shipment.request:{}", xml);
		SfKtsServiceProxy sfKtsServiceProxy = new SfKtsServiceProxy();
		try {
			String checkWord = apiCredentials.get(ApiCredential.KTS_B2C_CHECKWORD.getKey());
			String dig = digest(xml + checkWord, ENCODING);
			String response = sfKtsServiceProxy.sfKtsService(xml, dig);
			if (StringUtils.isBlank(response)) {
				return new ApiOrderResponse(false, null, ApiOrderError.SERVICE_NO_DATA_RETURN.getMessage(), true, null);
			}
			LOGGER.info("kts.b2c.shipment.response:{}", response);
			String head = extractContent("<Head>(.*)</Head>", response);
			if (OK.equals(head)) {
				String orderResponse = extractContent("<Body>(.*)</Body>", response);
				String referenceNo = extractContent("orderid=\"(.*)\"\\s+mailno", orderResponse);
				String thirdPartyorderNo = extractContent("mailno=\"(.*)\"\\s+agent_mailno", orderResponse);
				String trackNumber = extractContent("agent_mailno=\"(.*)\"\\s+direction_code", orderResponse);
				ApiOrderResponse apiOrderResponse = new ApiOrderResponse();
				apiOrderResponse.setSuccess(true);
				apiOrderResponse.setTrackNumber(trackNumber);
				apiOrderResponse.setThirdPartyorderNo(thirdPartyorderNo);
				ApiLabelResponse apiLabelResponse = label(referenceNo, thirdPartyorderNo, apiCredentials);
				if (StringUtils.isNotBlank(apiLabelResponse.getLabelPath())) {
					apiOrderResponse.setLabelUrl(apiLabelResponse.getLabelPath());
					apiOrderResponse.setLmLabelUrl(apiLabelResponse.getRelativePath());
				}
				return apiOrderResponse;
			} else if (ERR.equals(head)) {
				String error = extractContent("<ERROR .*?>(.*)</ERROR>", response);
				return new ApiOrderResponse(false, null, error, false, null);
			}
			return new ApiOrderResponse(false, null, ApiOrderError.SERVICE_NO_DATA_RETURN.getMessage(), true, null);
		} catch (RemoteException e) {
			LOGGER.error("kts.b2c.shipment.exception", e);
			return new ApiOrderResponse(false, null, ApiOrderError.CREATE_ORDER_EXCEPTION.getMessage(), true, null);
		}
	}

	/**
	 * <内容提取>
	 * 
	 * @param regex
	 * @param response
	 * @return
	 * @author Henry(fba02)
	 * @version [版本号, 2020-10-28]
	 * @see [类、类#方法、类#成员]
	 */
	public static String extractContent(String regex, String response) {
		Pattern pattern = Pattern.compile(regex);
		Matcher matcher = pattern.matcher(response);
		String content = "";
		while (matcher.find()) {
			content = matcher.group(1);
		}
		return content;
	}

	@Override
	public ApiLabelResponse label(String trackNumber, Map<String, String> configure) {
		return null;
	}

	@Override
	public ApiLabelResponse label(String referenceNo, String trackNumber, Map<String, String> configure) {
		try {
			String userName = configure.get(ApiCredential.KTS_B2C_NAME.getKey());
			String checkWord = configure.get(ApiCredential.KTS_B2C_CHECKWORD.getKey());
			String signature = digest(userName + checkWord, ENCODING);
			Map<String, String> params = new HashMap<String, String>();
			params.put("mailno", trackNumber); // 顺丰运单号组合
			params.put("orderid", referenceNo); // 客户订单号组合
			params.put("onepdf", "true"); // 是否打印一个pdf eg:true/false
			params.put("username", userName); // 接入编码
			params.put("jianhuodan", "false"); // 是否打印拣货单 eg:true/false
			params.put("signature", signature); // 签名
			// {"array":[{"mailno":"SF6042205577707","orderid":"TOFBA202010280834001","configId":14,"url":""}],"success":true,"version":"1603087525","url":"http:\/\/miandan-1251224359.cos.ap-shanghai.myqcloud.com\/Label595789727788833781.pdf"}
			HttpClientResult httpClientResult = HttpClientPoolUtil.doPost(LABEL, params, false);
			if (null == httpClientResult) {
				return new ApiLabelResponse(trackNumber, null);
			}
			String response = httpClientResult.getContent();
			if (StringUtils.isBlank(response)) {
				return new ApiLabelResponse(trackNumber, null);
			}
			Map<String, Object> responseMap = new Gson().fromJson(response, new TypeToken<Map<String, Object>>() {
			}.getType());
			Boolean success = (Boolean) responseMap.get("success");
			if (success != null && success) {
				String url = (String) responseMap.get("url");
				ApiLabelResponse apiLabelResponse = new ApiLabelResponse();
				apiLabelResponse.setBarCode(trackNumber);
				apiLabelResponse.setLabelPath(url);
				String pdfName = trackNumber + LabelSuffix.PDF;
				String centrePath = ApiUtil.getCurrentDay() + "/";
				String relativePath = "express_label/" + centrePath;
				String folderPath = DEFAULT_LABEL_ROOT + relativePath;
				if (!new File(folderPath).exists()) {
					new File(folderPath).mkdirs();
				}
				String fileUrl = folderPath + pdfName;
				try {
					FileUtils.copyURLToFile(new URL(url), new File(fileUrl));
					apiLabelResponse.setRelativePath(relativePath + pdfName);
				} catch (Exception e) {
					LOGGER.error("kts.b2c.copy.url.to.file.exception", e);
				}
				return apiLabelResponse;
			}
			return new ApiLabelResponse(trackNumber, null);
		} catch (Exception e) {
			LOGGER.error("kts.b2c.label.exception", e);
			return new ApiLabelResponse(trackNumber, null);
		}
	}

	@Override
	public ApiLabelResponse labelRender(String trackNumber, ExpressOrderApi expressOrderApi) {
		return null;
	}

	@Override
	public void tracking(String trackNumber, Map<String, String> configure) {

	}

	@Override
	public String trackNumber(String code, Map<String, String> configure) {
		return null;
	}

	private final static String ENCODING = "UTF-8";

	public static String digest(String toVerifyText, String encode) {
		String base64 = null;
		MessageDigest md5 = null;
		try {
			md5 = MessageDigest.getInstance("MD5");
			md5.update(toVerifyText.getBytes(encode));
			byte[] md = md5.digest();
			base64 = new String(Base64.encodeBase64(md), ENCODING);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return base64;
	}
}